#include "containers.h"
#include <stdlib.h>
#include <string.h>

CircleArray*
create_circle_array(const usize_t item_size, const usize_t array_size)
{
  CADATAPTR data = calloc(item_size, array_size);
  if (data) {
    CircleArray* ca = calloc(sizeof(CircleArray), 1);
    ca->item_size = item_size;
    ca->data_size = array_size;
    ca->data_ptr = data;
    return ca;
  } else {
    return NULL;
  }
}

void
ca_clear(CircleArray* ca)
{
  ca->full = ca->s = ca->e = ca->c = 0;
  ca->siz = 0;
}

void
init_circle_array(CircleArray* ca,
                  CADATAPTR data,
                  usize_t item_size,
                  usize_t array_size)
{
  ca_clear(ca);
  ca->item_size = item_size;
  ca->data_size = array_size;
  ca->data_ptr = data;
}

void
release_circle_array(CircleArray** ca)
{
  free((*ca)->data_ptr);
  free(*ca);
  *ca = NULL;
}

CADATAPTR
ca_get_data_ptr(const CircleArray* ca)
{
  return ca->data_ptr;
}

CADATAPTR
ca_get_data(const CircleArray* ca, const ca_iter it)
{
  if (it >= 0 && it < ca->data_size) {
    if (ca->siz > 0) {
      return ca->data_ptr + (it * ca->item_size);
    }
  }

  return NULL;
}

ca_iter
ca_front(const CircleArray* ca)
{
  if (ca->siz > 0) {
    return ca->s;
  }
  return ca_iter_end;
}

ca_iter
ca_back(const CircleArray* ca)
{
  if (ca->siz > 0) {
    return ca->c;
  }
  return ca_iter_end;
}
ca_iter
ca_current(const CircleArray* ca)
{
  return ca_back(ca);
}

ca_iter
ca_next(const CircleArray* ca, const ca_iter it)
{
  if (ca->siz < 2 || it < 0 || it >= ca->data_size)
    return ca_iter_end;

  ca_iter nn = it + 1;
  if ((ca->siz >= ca->data_size) && (nn >= ca->data_size))
    nn = 0;

  if (ca->full)
    return (nn != ca->s) ? nn : ca_iter_end;
  else
    return (nn < ca->e) ? nn : ca_iter_end;
}

ca_iter
ca_prev(const CircleArray* ca, const ca_iter it)
{
  if (ca->siz < 2 || it < 0 || it >= ca->data_size)
    return ca_iter_end;

  ca_iter nn = it - 1;
  if ((ca->siz >= ca->data_size) && (nn < 0))
    nn = ca->data_size - 1;
  return ((nn < it && nn < ca->c) || (nn >= ca->s && nn != ca->c))
           ? nn
           : ca_iter_end;
}

/*
 |-----------------------------------------------------------------\
 |    |  c  | e | s |                                             |
 |-----------------------------------------------------------------/
*/
ca_iter
ca_next_(const CircleArray* ca, const ca_iter it)
{
  ca_iter nn = it + 1;
  if ((ca->full) && (nn >= ca->data_size))
    nn = 0;

  if (ca->full)
    return (nn != ca->s) ? nn : ca_iter_end;
  else
    return (nn < ca->e) ? nn : ca_iter_end;
}

ca_iter
ca_prev_(const CircleArray* ca, const ca_iter it)
{
  ca_iter nn = it - 1;
  if ((ca->full) && (nn < 0))
    nn = ca->data_size - 1;
  return ((nn < it && nn < ca->c) || (nn >= ca->s && nn != ca->c))
           ? nn
           : ca_iter_end;
}

ca_iter
ca_new_data(CircleArray* ca, const CADATAPTR data)
{
  memcpy(ca->data_ptr + ca->e * ca->item_size, data, ca->item_size);

  ca->c = ca->e;
  if (ca->full) {
    ++ca->s;
  }
  ++ca->e;

  if (ca->e >= ca->data_size) {
    ca->e = 0;
  }
  if (ca->s >= ca->data_size) {
    ca->s = 0;
  }

  if (!ca->full) {
    ++ca->siz;
    ca->full = (ca->siz >= ca->data_size);
  }

  return ca->c;
}

ca_iter
ca_pop_back(CircleArray* ca)
{
  return ca_iter_end;
}

ca_iter
ca_pop_front(CircleArray* ca)
{
  if (ca->siz < 1)
    return ca_iter_end;

  ca_iter front = ca->s;
  if (front != ca_iter_end) {
    ++front;
    if (front >= ca->data_size) {
      front = 0;
    }
    ca->s = front;
    --ca->siz;
    if (ca->siz < 1) {
      ca->c = ca->s;
      return ca_iter_end;
    } else {
      return ca->s;
    }
  }
  return ca_iter_end;
}

ca_iter
ca_delete(CircleArray* ca, const ca_iter it)
{
  return ca_iter_end;
}

CADATAPTR
ca_data(const CircleArray* ca, const ca_iter it)
{
  return ca->data_ptr + (it * ca->item_size);
}

int
ca_remove(CircleArray* ca, const ca_iter it)
{
  return (0);
}
